﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Net;
using System.Xml;

namespace PUCLibrary
{
    public static class PucParser
    {
        public static List<Ramo> Parse(string url, string carrera)
        {
            List<Ramo> result = new List<Ramo>();
            string htmlData = null;
            try
            {
                htmlData = new WebClient().DownloadString(url);  //Cargamos la pagina web a un string.
            }
            catch (WebException err)
            {
                Logger.Instance.Log("Error descargando ramos de " + carrera, err);
                return result;
            }
            
            HTMLSearchResult searchData = new HTMLSearchResult(htmlData).GetTagData("html");
            if (!String.IsNullOrEmpty(searchData.TAGData))
            {
                List<HTMLSearchResult> tables = searchData.GetAllTagData("table");  //Obtenemos todas las tablas

                if (tables.Count % 2 != 0)
                    throw new Exception("Hay un numero impar de tablas, la pagina esta incompleta o algo!");

                for (int i = 0; i < tables.Count; i += 2)  //Recorremos todas las tablas, de a pares
                {
                    //Primera tabla (Datos del ramo)
                    string siglaYNombreRaw = tables[i].GetTagData("font").TAGData; //Ejemplo de input: <a name="iac3210"></a> Gestion y mejoramiento de la calidad "
                    string descripcionRaw = tables[i].GetTagData("td", 4).TAGData; //Aqui viene num. creditos + descripcion
                    string urlProgramaRaw = tables[i].GetTagData("td").GetTagData("font").TAGData;

                    Ramo ramoActual = ParseRamo(siglaYNombreRaw, descripcionRaw, urlProgramaRaw, carrera);

                    //Segunda tabla (Datos de las secciones)
                    List<HTMLSearchResult> seccionData = tables[i + 1].GetAllTagData("tr");

                    //Antes de empezar a iterar secciones, vemos el tipo de tabla.
                    //Hay 2 tipos de tabla (cambia el num. de columnas que tiene "Vacantes ofrecidas/disponibles"
                    int offset = 0;
                    if (seccionData[1].GetAllTagData("th").Count == 7)
                        offset = -2;

                    //Recorremos cada seccion del ramo (secN = 2, pq nos saltamos los headers de la tabla)
                    for (int secN = 2; secN < seccionData.Count; secN++)
                    {
                        string numSeccionRaw = seccionData[secN].GetTagData("font", 1).TAGData;
                        if (numSeccionRaw.StartsWith("<b>Obs:"))
                            continue;   //No es una seccion valida, solo es un comentario en la tabla.

                        string numVacantesRaw = seccionData[secN].GetTagData("font", 2).TAGData;
                        string horarioRaw = seccionData[secN].GetTagData("font", 5 + offset).TAGData;    //Ej: L-W:3<br>V:3<br>
                        string horarioNombreRaw = seccionData[secN].GetTagData("font", 6 + offset).TAGData;  //Ej: CAT<br>AYUD<br>
                        string salaRaw = seccionData[secN].GetTagData("font", 7 + offset).TAGData;   //Ej: A3<br>A3
                        string profesorRaw = seccionData[secN].GetTagData("font", 8 + offset).TAGData;   //Ej: - Cienfuegos Rodrigo<br>
                        string campusRaw = seccionData[secN].GetTagData("font", 9 + offset).TAGData; //Ej: SJ
                        string cortePeriodoRaw = seccionData[secN].GetTagData("font", 10 + offset).TAGData;  //Ej: 2-2008<br>1-2009
                        string cortePtjeRaw = seccionData[secN].GetTagData("font", 11 + offset).TAGData; //Ej:     588<br>    622
                        string evaluacionesRaw = seccionData[secN].GetTagData("font", 14 + offset).TAGData;  //Ej: 31-Ago<br>5-Oct<br>2-Nov<br>9-Dic

                        //Ahora desHTMLizamos la info
                        try
                        {
                            Seccion seccionActual = ParseSeccion(numSeccionRaw, numVacantesRaw, horarioRaw, horarioNombreRaw, salaRaw, profesorRaw, campusRaw, cortePeriodoRaw, cortePtjeRaw, evaluacionesRaw);
                        
                            //Bien! Tenemos toda la info, ahora creamos el objeto.
                            ramoActual.AddSeccion(seccionActual);
                        }
                        catch (Exception e)
                        {
                            Logger.Instance.Log("Error de parseo: " + ramoActual.Sigla, e);
                            //System.Diagnostics.Debugger.Log(1, "Error de parseo: (" + siglaYNombreRaw + ")\n", e + "\n");
                        }

                    }
                    result.Add(ramoActual);
                }
            }
            else
                throw new ArgumentException("No es una pagina valida del DARA.");

            return result;
        }

        private static Ramo ParseRamo(string siglaYNombreRaw, string descripcionRaw, string urlProgramaRaw, string carrera)
        {
            string[] delimiter = { "<br>", "<br/>" };

            //Sigla:
            int index1 = siglaYNombreRaw.IndexOf('"');
            int index2 = siglaYNombreRaw.IndexOf('"', index1 + 1);
            string sigla = siglaYNombreRaw.Substring(index1 + 1, index2 - index1 - 1).ToUpper();
            //Nombre:
            string nombre = "??? (error)";
            int index3 = siglaYNombreRaw.LastIndexOf('>');
            if (index3 != -1 && index3 != siglaYNombreRaw.Length - 1)
                nombre = HtmlEntityParser.DeEntitize(siglaYNombreRaw.Substring(index3 + 1).Trim());
            //Descripcion + creditos:
            int creditos = -1;
            string descripcion = HtmlEntityParser.DeEntitize(descripcionRaw.Replace("<br>", "\r\n"));
            if (descripcion == null)
                descripcion = "(sin descripcion)";
            else
            {
                creditos = Int32.Parse(descripcion.Substring(1, descripcion.IndexOf("cr.)") - 2));
                descripcion = descripcion.Substring(descripcion.IndexOf(")")+1).Trim();
            }
            //URL programa del curso:
            string urlPrograma = "";
            if (urlProgramaRaw != "Programa no disponible")
            {
                int s1 = urlProgramaRaw.IndexOf('"') + 1;
                int s2 = urlProgramaRaw.IndexOf("\">Programa");
                urlPrograma = urlProgramaRaw.Substring(s1, s2 - s1);
            }

            return new Ramo(sigla, nombre, descripcion.Trim(), carrera, creditos, urlPrograma);
        }

        private static Seccion ParseSeccion(string numSeccionRaw, string numVacantesRaw, string horarioRaw, string horarioNombreRaw, 
            string salaRaw, string profesorRaw, string campusRaw, string cortePeriodoRaw, string cortePtjeRaw, string evaluacionesRaw)
        {
            string[] delimiter = {"<br>", "<br/>"};

            //Numeros:
            int numSeccion = Int32.Parse(numSeccionRaw);
            int numVacantes = Int32.Parse(numVacantesRaw);
            //Parsear horario:
            string[] modulo = HtmlEntityParser.DeEntitize(horarioRaw).Split(delimiter, StringSplitOptions.RemoveEmptyEntries);
            string[] moduloNombre = HtmlEntityParser.DeEntitize(horarioNombreRaw).Split(delimiter, StringSplitOptions.RemoveEmptyEntries);
            string[] moduloSala = HtmlEntityParser.DeEntitize(salaRaw).Split(delimiter, StringSplitOptions.RemoveEmptyEntries);
            Horario horario = new Horario(modulo, moduloNombre, moduloSala);
            //Profesor(es):
            string[] profesor = HtmlEntityParser.DeEntitize(profesorRaw).Split(delimiter, StringSplitOptions.RemoveEmptyEntries);
            for (int i = 0; i < profesor.Length; i++)
            {
                profesor[i] = profesor[i].Trim('-', ' ');
            }
            string campus = campusRaw.Replace("<br>", "");
            //TODO: ptjes. de corte
            //Evaluaciones:
            string[] evaluacionesRaw2 = evaluacionesRaw.Split(delimiter, StringSplitOptions.RemoveEmptyEntries);
            List<DateTime> evaluaciones = new List<DateTime>();
            if (evaluacionesRaw2.Length > 0 && evaluacionesRaw2[0] != "&nbsp;&nbsp;")  //Nos saltamos las evaluaciones invalidas
            {
                //evaluaciones = new DateTime[evaluacionesRaw2.Length];
                for (int i = 0; i < evaluacionesRaw2.Length; i++)
                {
                    DateTime dt;
                    if (DateTime.TryParse(evaluacionesRaw2[i].Replace("-", " "), out dt))
                        evaluaciones.Add(dt);

                    //evaluaciones[i] = DateTime.Parse(evaluacionesRaw2[i].Replace("-", " "));
                }
            }
            //else
            //    evaluaciones = new DateTime[0];

            return new Seccion(numSeccion, numVacantes, horario, profesor, campus, evaluaciones.ToArray());
        }

    }
}
